import {
  Injectable,
  BadRequestException,
  NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as fs from 'fs';
import * as path from 'path';
import { promisify } from 'util';
import * as AdmZip from 'adm-zip';
import * as sourceMap from 'source-map';

import { PlatformType, Sourcemap } from './entities/sourcemap.entity';
import { CreateSourcemapDto } from './dto/create-sourcemap.dto';
import { LocateErrorDto } from './dto/locate-error.dto';
import { ErrorLocationVo } from './vo/error-location.vo';
import { ListSourcemapDto } from './dto/list-sourcemap.dto';
import { ListSourcemapVo } from './vo/list-sourcemap.vo';

const mkdir = promisify(fs.mkdir);
const writeFile = promisify(fs.writeFile);
const readFile = promisify(fs.readFile);
const unlink = promisify(fs.unlink);

@Injectable()
export class SourcemapService {
  constructor(
    @InjectRepository(Sourcemap)
    private readonly sourcemapRepository: Repository<Sourcemap>,
  ) {}

  private async ensureUploadDir(projectType: string): Promise<string> {
    const uploadDir = path.join(
      process.cwd(),
      'uploads',
      'sourcemaps',
      projectType,
    );
    await mkdir(uploadDir, { recursive: true });
    return uploadDir;
  }

  async create(
    file: Express.Multer.File,
    createSourcemapDto: CreateSourcemapDto,
    user,
  ): Promise<void> {
    const { platformType, customerName, projectType, version } =
      createSourcemapDto;

    // 验证文件
    if (!file) {
      throw new BadRequestException('请上传文件');
    }

    if (!file.originalname.endsWith('.zip')) {
      throw new BadRequestException('只支持上传 zip 文件');
    }

    // 验证私有云客户名称
    if (platformType === PlatformType.CUSTOM && !customerName) {
      throw new BadRequestException('私有云部署必须提供客户名称');
    }

    // 确保上传目录存在
    const uploadDir = await this.ensureUploadDir(projectType);

    // 生成文件名和路径
    const filename = `${projectType}_${version}_${Date.now()}.zip`;
    const filepath = path.join(uploadDir, filename);

    // 保存文件
    await writeFile(filepath, file.buffer);

    // 保存记录到数据库
    const sourcemap = this.sourcemapRepository.create({
      filename,
      filepath,
      platformType,
      customerName: customerName || '',
      projectType,
      version,
    });

    await this.sourcemapRepository.save(sourcemap);
  }

  async findAll(): Promise<Sourcemap[]> {
    return this.sourcemapRepository.find({
      order: {
        createTime: 'DESC',
      },
    });
  }

  async findOne(id: number): Promise<Sourcemap> {
    const sourcemap = await this.sourcemapRepository.findOne({
      where: { id },
    });

    if (!sourcemap) {
      throw new NotFoundException('Sourcemap 文件不存在');
    }

    return sourcemap;
  }

  async remove(id: number): Promise<void> {
    const sourcemap = await this.findOne(id);

    // 删除文件
    try {
      await promisify(fs.unlink)(sourcemap.filepath);
    } catch (error) {
      console.error('删除文件失败:', error);
    }

    // 删除数据库记录
    await this.sourcemapRepository.remove(sourcemap);
  }

  async locateError(locateErrorDto: LocateErrorDto): Promise<ErrorLocationVo> {
    const { sourcemapId, bugId, line, column } = locateErrorDto;

    // 从数据库查找对应的 sourcemap 文件
    const sourcemap = await this.sourcemapRepository.findOne({
      where: { id: sourcemapId },
    });

    if (!sourcemap) {
      throw new NotFoundException('未找到对应的 sourcemap 文件');
    }

    try {
      // 解压 zip 文件
      const zip = new AdmZip(sourcemap.filepath);
      const zipEntries = zip.getEntries();

      // 查找匹配 bugId 的 .map 文件
      const mapEntry = zipEntries.find(
        (entry) =>
          entry.entryName.endsWith('.map') && entry.entryName.includes(bugId),
      );

      if (!mapEntry) {
        throw new BadRequestException(`未找到匹配 ${bugId} 的 .map 文件`);
      }

      // 解析 sourcemap
      const consumer = await new sourceMap.SourceMapConsumer(
        mapEntry.getData().toString('utf8'),
      );

      // 获取原始位置
      const originalPosition = consumer.originalPositionFor({
        line: line,
        column: column,
      });

      if (!originalPosition.source) {
        throw new BadRequestException('无法定位到源代码位置');
      }

      // 获取源代码内容
      const sourceContent = consumer.sourceContentFor(originalPosition.source);
      let sourceLine: string | undefined;
      let contextLines: string[] | undefined;

      if (sourceContent) {
        const lines = sourceContent.split('\n');
        if (originalPosition.line && originalPosition.line <= lines.length) {
          sourceLine = lines[originalPosition.line - 1];
          // 获取上下文代码（前后各2行）
          const start = Math.max(0, originalPosition.line - 3);
          const end = Math.min(lines.length, originalPosition.line + 2);
          contextLines = lines.slice(start, end);
        }
      }

      // 释放 consumer
      consumer.destroy();

      return {
        source: originalPosition.source,
        line: originalPosition.line || line,
        column: originalPosition.column || column,
        name: originalPosition.name || undefined,
        sourceContent: contextLines?.join('\n'),
        sourceLine: sourceLine,
      };
    } catch (error) {
      console.error('解析 sourcemap 失败:', error);
      throw new BadRequestException('解析 sourcemap 失败: ' + error.message);
    }
  }

  async getList(listSourcemapDto: ListSourcemapDto): Promise<ListSourcemapVo> {
    const {
      page = 1,
      pageSize = 10,
      platformType,
      customerName,
      projectType,
    } = listSourcemapDto;
    const skip = (page - 1) * pageSize;

    const queryBuilder = this.sourcemapRepository
      .createQueryBuilder('sourcemap')
      .orderBy('sourcemap.createTime', 'DESC')
      .skip(skip)
      .take(pageSize);

    // 如果有平台类型筛选
    if (platformType) {
      queryBuilder.andWhere('sourcemap.platformType = :platformType', {
        platformType,
      });
    }

    // 如果有客户名称筛选
    if (customerName) {
      queryBuilder.andWhere('sourcemap.customerName LIKE :customerName', {
        customerName: `%${customerName}%`,
      });
    }

    // 如果有项目类型筛选
    if (projectType) {
      queryBuilder.andWhere('sourcemap.projectType = :projectType', {
        projectType,
      });
    }

    const [list, total] = await queryBuilder.getManyAndCount();

    return {
      list,
      total,
      page,
      pageSize,
    };
  }
}
